﻿namespace AutoRevit.Event;

/// <summary>
/// 外部事件任务
/// </summary>
public class ExternalEventTask
{
    internal readonly CustomEventHandler handler;

    internal readonly ExternalEvent externalEvent;

    internal TaskCompletionSource<object> tcs = null!;

    /// <summary>
    /// 实例化revit外部命令任务
    /// </summary>
    public ExternalEventTask()
    {
        handler = new CustomEventHandler();

        handler.EventCompleted += OnEventCompleted;

        externalEvent = ExternalEvent.Create(handler);
    }

    private void OnEventCompleted(object? sender, object result)
    {
        if (handler.Exception == null)
        {
            tcs.TrySetResult(result);
        }
        else
        {
            tcs.TrySetException(handler.Exception);
        }
    }

    /// <summary>
    /// 执行外部事件任务
    /// </summary>
    /// <param name="act"></param>
    /// <returns></returns>
    public Task RunAsync(Action<UIApplication> act)
    {
        tcs = new TaskCompletionSource<object>();
        Func<UIApplication, object> f = app => { act(app); return new object(); };
        handler.SetFunc(f);
        externalEvent.Raise();
        return tcs.Task;
    }

    /// <summary>
    /// 执行可以返回数据的外部事件任务
    /// </summary>
    /// <typeparam name="TResult"></typeparam>
    /// <param name="func"></param>
    /// <returns></returns>
    public Task<TResult> RunAsync<TResult>(Func<UIApplication, TResult> func)
    {
        tcs = new TaskCompletionSource<object>();
        var task = Task.Run(async () => (TResult)await tcs.Task);
        handler.SetFunc(func);
        externalEvent.Raise();
        return task;
    }

    internal sealed class CustomEventHandler : IExternalEventHandler
    {
        private Delegate? func;

        private object?[]? parameters;

        public event EventHandler<object>? EventCompleted;

        public Exception? Exception { get; private set; }

        public void Execute(UIApplication app)
        {
            object? result = null;

            Exception = null;

            try
            {
                if (parameters is null || !parameters.Any())
                {
                    result = func!.DynamicInvoke(app);
                }
                else
                {
                    List<object?> tempLstParam = [app, .. parameters];
                    var param = tempLstParam?.Select(t => t is null ? t : Convert.ChangeType(t, t.GetType())).ToArray();

                    result = func!.DynamicInvoke(param);
                }
            }
            catch (Exception ex)
            {
                Exception = ex;
            }
            finally
            {
                func = null;
                parameters = null;
                GC.Collect();
            }

            EventCompleted?.Invoke(this, result!);
        }

        public string GetName()
        {
            return "RevitTask";
        }

        internal void SetFunc(Delegate func, params object?[] parameters)
        {
            this.func = func;
            this.parameters = parameters;
        }
    }
}
